package service

import (
	"fmt"
	"github.com/astaxie/beego/logs"
	"github.com/garyburd/redigo/redis"
	"strconv"
	"time"
)

func InitService(serviceConf *SecKillConf) {
	secKillConf = serviceConf
	err := loadBlackList()
	if err != nil {
		logs.Error("load black list err:%v", err)
		return
	}
	logs.Debug("init service success,config:%v", secKillConf)

	err = initProxy2LayerRedis()
	if err != nil {
		logs.Error("load proxy2layer redis failed err:%v", err)
		return
	}

	secKillConf.secLimitMgr = &SecLimitMgr{
		UserLimitMap: make(map[int]*SecLimit, 10000),
		IpLimitMap:   make(map[string]*SecLimit, 10000),
	}

	secKillConf.SecReqChan = make(chan *SecRequest, secKillConf.SecReqChanSize)

	err = initRedisProcessFunc()
	if err != nil {
		logs.Error("init initRedisProcessFunc redis pool failed,err:%v", err)
		return
	}

	return
}

func initRedisProcessFunc() (err error) {
	for i := 0; i < secKillConf.WriteProxy2LayerGoroutineNum; i++ {
		go WriteHandle()
	}
	for i := 0; i < secKillConf.ReadLayer2ProxyGoroutineNum; i++ {
		go ReadHandle()
	}
	return nil
}

func loadBlackList() (err error) {
	err = initBlackRedis()
	if err != nil {
		logs.Error("init balck redis failed,err:%v", err)
		return
	}
	conn := secKillConf.BlackRedisPool.Get()
	defer conn.Close()

	reply, err := conn.Do("hgetall", "idblacklist")

	idList, err := redis.Strings(reply, err)
	if err != nil {
		logs.Warn("hget all failed,err:%v", err)
		return
	}
	for _, v := range idList {
		id, err := strconv.Atoi(v)
		if err != nil {
			logs.Warn("invalid user id")
			continue
		}
		secKillConf.IdBlackMap[id] = true
	}

	reply, err = conn.Do("hgetall", "ipblacklist")

	ipList, err := redis.Strings(reply, err)
	if err != nil {
		logs.Warn("hget all failed,err:%v", err)
		return
	}
	for _, v := range ipList {
		secKillConf.IpBlackMap[v] = true
	}

	go SyncIdBlackList()
	go SyncIpBlackList()
	return
}
func SyncIpBlackList() {
	var ipList []string
	lastTime := time.Now().Unix()
	for {
		conn := secKillConf.BlackRedisPool.Get()
		defer conn.Close()
		reply, err := conn.Do("BLPOP", "blackiplist", time.Second)
		ip, err := redis.String(reply, err)
		if err != nil {
			continue
		}
		ipList = append(ipList, ip)
		curTime := time.Now().Unix()
		if len(ipList) > 100 || curTime-lastTime > 5 {
			secKillConf.RWBlackLock.Lock()
			for _, v := range ipList {
				secKillConf.IpBlackMap[v] = true
			}
			secKillConf.RWBlackLock.Unlock()

			lastTime = time.Now().Unix()
			logs.Info("sync ip list from reids success,ip[%v]", ipList)
		}

	}
}
func SyncIdBlackList() {
	var idList []int
	lastTime := time.Now().Unix()
	for {
		conn := secKillConf.BlackRedisPool.Get()
		defer conn.Close()
		reply, err := conn.Do("BLPOP", "blackidlist", time.Second)
		id, err := redis.String(reply, err)
		if err != nil {
			continue
		}
		intId, err := strconv.Atoi(id)
		if err != nil {
			continue
		}
		idList = append(idList, intId)
		curTime := time.Now().Unix()
		if len(idList) > 100 || curTime-lastTime > 5 {
			secKillConf.RWBlackLock.Lock()
			for _, v := range idList {
				secKillConf.IdBlackMap[v] = true
			}
			secKillConf.RWBlackLock.Unlock()

			lastTime = time.Now().Unix()
			logs.Info("sync id list from reids success,ip[%v]", idList)
		}

	}
}
func initProxy2LayerRedis() (err error) {
	secKillConf.Proxy2LayerRedisPool = &redis.Pool{
		MaxIdle:     secKillConf.RedisBlackConf.MaxIdle,
		MaxActive:   secKillConf.RedisBlackConf.MaxActive,
		IdleTimeout: time.Duration(secKillConf.RedisBlackConf.IdleTimeout) * time.Second,
		Dial: func() (redis.Conn, error) {
			return redis.Dial("tcp", secKillConf.RedisBlackConf.Addr)
		},
	}
	conn := secKillConf.BlackRedisPool.Get()
	defer conn.Close()
	reply, err := conn.Do("ping")
	fmt.Printf("redis ping reply: %v ", reply)
	if err != nil {
		logs.Error("redis conn error:%v,reply:%v", err, reply)
		return
	}
	return
}
func initBlackRedis() (err error) {
	secKillConf.BlackRedisPool = &redis.Pool{
		MaxIdle:     secKillConf.RedisBlackConf.MaxIdle,
		MaxActive:   secKillConf.RedisBlackConf.MaxActive,
		IdleTimeout: time.Duration(secKillConf.RedisBlackConf.IdleTimeout) * time.Second,
		Dial: func() (redis.Conn, error) {
			return redis.Dial("tcp", secKillConf.RedisBlackConf.Addr)
		},
	}
	conn := secKillConf.BlackRedisPool.Get()
	defer conn.Close()
	reply, err := conn.Do("ping")
	fmt.Printf("redis ping reply: %v ", reply)
	if err != nil {
		logs.Error("redis conn error:%v,reply:%v", err, reply)
		return
	}
	return
}
